# cython: language_level=3

from libc.stdint cimport uintptr_t
from libc.string cimport strdup
from ctypes import CFUNCTYPE, c_size_t, c_uint, c_void_p, c_char_p

cdef extern from "<stdlib.h>":
    void* malloc(size_t size)
    void free(void* ptr)

cdef extern from "hs/hs.h":
    cdef enum:
        HS_FLAG_CASELESS
        HS_FLAG_DOTALL
        HS_FLAG_MULTILINE
        HS_FLAG_SINGLEMATCH
        HS_FLAG_ALLOWEMPTY
        HS_FLAG_UTF8
        HS_FLAG_UCP
        HS_FLAG_PREFILTER
        HS_FLAG_SOM_LEFTMOST
        HS_FLAG_COMBINATION
        HS_SUCCESS

    ctypedef struct hs_database_t:
        pass

    ctypedef struct hs_scratch_t:
        pass

    ctypedef struct hs_compile_error_t:
        const char *message  # 错误信息
        const char *expression  # 出错的正则表达式

    ctypedef struct hs_expr_info_t:
        unsigned int min_width
        unsigned int max_width
        char unordered_matches
        char matches_at_eod
        char matches_only_at_eod

    int HS_SCAN_TERMINATED = HS_SCAN_TERMINATED
    int HS_MODE_BLOCK = HS_MODE_BLOCK

    int hs_compile(char *pattern,
                   unsigned int flags,
                   unsigned int mode,
                   void *platform,
                   hs_database_t **db,
                   hs_compile_error_t **err)
    int hs_expression_info(const char *expression,
                                       unsigned int flags,
                                       hs_expr_info_t **info,
                                       hs_compile_error_t **error)
    int hs_compile_multi(const char *const *expressions,
                         const unsigned int *flags,
                         const unsigned int *ids,
                         unsigned int elements,
                         unsigned int mode,
                         const void *platform,
                         hs_database_t **db,
                         hs_compile_error_t **error);
    int hs_alloc_scratch(hs_database_t *db, hs_scratch_t **scratch)
    int hs_scan(hs_database_t *db, char *data, size_t length, unsigned int flags, hs_scratch_t *scratch, void *callback, void *context)
    void hs_free_scratch(hs_scratch_t *scratch)
    void hs_free_database(hs_database_t *db)
    void hs_free_compile_error(hs_compile_error_t *err)  # 释放错误信息

# 将 Hyperscan 的标志映射到 Python 的定义码
HS_FLAG_CASELESS_PY = HS_FLAG_CASELESS
HS_FLAG_DOTALL_PY = HS_FLAG_DOTALL
HS_FLAG_MULTILINE_PY = HS_FLAG_MULTILINE
HS_FLAG_SINGLEMATCH_PY = HS_FLAG_SINGLEMATCH
HS_FLAG_ALLOWEMPTY_PY = HS_FLAG_ALLOWEMPTY
HS_FLAG_UTF8_PY = HS_FLAG_UTF8
HS_FLAG_UCP_PY = HS_FLAG_UCP
HS_FLAG_PREFILTER_PY = HS_FLAG_PREFILTER
HS_FLAG_SOM_LEFTMOST_PY = HS_FLAG_SOM_LEFTMOST
HS_FLAG_COMBINATION_PY = HS_FLAG_COMBINATION
HS_SUCCESS_PY = HS_SUCCESS

# 定义回调函数类型
cdef extern void match_callback(unsigned int id, size_t from_, size_t to, unsigned int flags, void *context)

# 定义回调函数的 C 类型
ctypedef void (*CALLBACK_TYPE)(c_uint, c_size_t, c_size_t, c_uint, c_void_p)
# 定义 C 回调函数的原型
c_callback_type = CFUNCTYPE(None, c_uint, c_size_t, c_size_t, c_uint, c_void_p)

# 编译正则表达式
def compile_pattern(pattern, flags=0, mode=HS_MODE_BLOCK):
    cdef hs_database_t *db
    cdef hs_compile_error_t *err
    cdef int ret

    # 调用 hs_compile
    ret = hs_compile(pattern, flags, mode, NULL, &db, &err)

    # 检查 hs_compile 的返回值
    if ret != 0:
        print(f"hs_compile failed with error code {ret}")
        if err:
            print(f"Error message: {err.message}")
            hs_free_compile_error(err)
        return 0  # 返回 0 表示编译失败

    # 返回数据库指针
    return <uintptr_t>db

def check_patterns(patterns: list, flags: list):
    """
    检查一组正则表达式和标志是否有效，并返回所有无效的正则表达式及其错误信息。

    :param patterns: 正则表达式列表（bytes 类型）
    :param flags: 对应的标志列表
    :return: (bool, list) - 第一个值为是否全部有效，第二个值为无效的正则表达式及其错误信息的列表
    """
    if len(patterns) != len(flags):
        return False, ["Patterns and flags must have the same length."]

    invalid_patterns = []  # 存储无效的正则表达式及其错误信息

    cdef hs_expr_info_t *pRegexInfo = NULL
    cdef hs_compile_error_t *compile_err = NULL
    cdef int hsError

    for i, (pattern, flag) in enumerate(zip(patterns, flags)):
        # 调用 hs_expression_info 检查正则表达式
        hsError = hs_expression_info(pattern.encode('utf-8'), flag & (~HS_FLAG_COMBINATION), &pRegexInfo, &compile_err)

        if hsError != HS_SUCCESS:
            # 如果检查失败，记录错误信息
            if compile_err:
                error_msg = f"Pattern '{pattern}' is invalid: {compile_err.message.decode('utf-8')}"
                hs_free_compile_error(compile_err)
            else:
                error_msg = f"Pattern '{pattern}' is invalid: Unknown error."
            invalid_patterns.append((pattern, error_msg))
        else:
            # 如果检查成功，释放 pRegexInfo
            if pRegexInfo:
                free(pRegexInfo)

    # 如果没有无效的正则表达式，返回 True
    if not invalid_patterns:
        return True, []
    else:
        return False, invalid_patterns

# 编译正则表达式
def compile_multi_patterns(patterns: list, flags: list, ids: list, mode=HS_MODE_BLOCK):
    len_patterns = len(patterns)
    len_flags = len(flags)
    len_ids = len(ids)

    if not (len_patterns == len_flags == len_ids):
        return -1

    cdef hs_database_t *db
    cdef hs_compile_error_t *err
    cdef int ret

    # 将 patterns 转换为 C 字符串数组
    cdef char **c_patterns = <char**>malloc(sizeof(char*) * len(patterns))
    for i, pattern in enumerate(patterns):
        c_patterns[i] = strdup(pattern.encode('utf-8'))  # 使用 strdup 复制字符串
    # 将 flags 和 ids 转换为 ctypes 数组
    cdef unsigned int *c_ids = <unsigned int*>malloc(sizeof(unsigned int) * len(ids))
    cdef unsigned int *c_flags = <unsigned int*>malloc(sizeof(unsigned int) * len(flags))

    for i, val in enumerate(ids):
        c_ids[i] = val
        c_flags[i] = flags[i]

    # 调用 hs_compile
    ret = hs_compile_multi(<const char **>c_patterns, <const unsigned int*>c_flags, <const unsigned int*>c_ids, len(ids), mode, NULL, &db, &err)
    for i in range(len_patterns):
        free(c_patterns[i])
    free(c_patterns)
    free(c_flags)
    free(c_ids)
    # 检查 hs_compile 的返回值
    if ret != 0:
        print(f"hs_compile failed with error code {ret}")
        if err:
            print(f"Error message: {err.message}")
            hs_free_compile_error(err)
        return 0  # 返回 0 表示编译失败

    # 返回数据库指针
    return <uintptr_t>db

# 定义 C 回调函数
cdef int c_match_callback(unsigned int id, size_t from_, size_t to, unsigned int flags, void *context):
    # 将 C 上下文转换为 Python 对象
    cdef object py_callback = <object>context
    # 调用 Python 回调函数
    py_callback(id, from_, to, flags)
    return 0

# 扫描数据
def scan_data(uintptr_t db, data, callback):
    cdef hs_scratch_t *scratch = <hs_scratch_t*>(0)
    cdef int ret

    # 检查 db 是否为 NULL
    if db == 0:
        print("Database is NULL. Compile pattern first.")
        return

    # 分配 scratch 空间
    ret = hs_alloc_scratch(<hs_database_t*>db, &scratch)
    if ret != 0:
        print(f"hs_alloc_scratch failed with error code {ret}")
        return

    # 将 Python 回调函数包装为 C 回调函数
    cdef object py_callback = callback  # 保存 Python 回调函数
    encode_data = data.encode('utf-8')

    # 调用 hs_scan
    ret = hs_scan(<hs_database_t*>db, encode_data, len(encode_data), 0, scratch, <void*>c_match_callback, <void*>py_callback)
    if ret == HS_SCAN_TERMINATED:
        print("Scan terminated by callback.")
    elif ret != 0:
        print(f"hs_scan failed with error code {ret}")

    # 释放 scratch 空间
    hs_free_scratch(scratch)

# 释放数据库
def free_database(uintptr_t db):
    hs_free_database(<hs_database_t*>db)